#include <SFML/Graphics.hpp>
#include <time.h>
#include <iostream>
using namespace std;
using namespace sf;

// 场景大小
const int M = 20;
const int N = 10;
// 场景内的位置初始化0
int field[M][N] = {0};

// 方块每个点相对位置
struct Point
{
	int x, y;
} a[4], b[4];
Point c[4] = {0, 0};
// 方块形状描述
/*
0 1 	0 0
2 3 	0 1
4 5 	1 1
6 7 	0 1  (3,4,5,7)
*/
int figures[7][4] =
{
	/*第二列是旋转中心*/
	1, 3, 5, 7, // I
	2, 4, 5, 7, // Z
	3, 5, 4, 6, // S
	3, 5, 4, 7, // T
	2, 3, 5, 7, // L
	3, 5, 7, 6, // J
	2, 3, 4, 5, // O
};

// 越界检查
bool check()
{
	for (int i = 0; i < 4; i++)
		// 到达边界
		if (a[i].x < 0 || a[i].x >= N || a[i].y >= M)
			return 0;
	// field 内的位置都为0，不为零说明数组越界 或 位置有方块
		else if (field[a[i].y][a[i].x])
			return 0;

	return 1;
};

// blockOfGeneration
void makeblockS()
{
	int n = rand() % 7; // 随机产生方块
	// 计算出每个形状方块的相对坐标(左上角为[0,0])
	// 每个形状有4块 x可取{0，1} y可取{0,1,2 3}
	for (int i = 0; i < 4; i++) // nb!!
	{
		// 对2取余 得到在第几列，0或1
		a[i].x = 3 + figures[n][i] % 2; // +3 使初始位置在中间
		// 对2取商 得到在第几行，0~3
		a[i].y = figures[n][i] / 2;
		// ps: 7描述时在右下角，所以在第二列，第三行 [1,3]
	}
}

int main()
{
	srand(time(0));

	// 窗口变量 宽度和高
	RenderWindow window(VideoMode(320, 480), "The Game!");

	Texture t1, t2, t3;							 // 创建纹理
	t1.loadFromFile("../images/tiles.png");		 // 方块
	t2.loadFromFile("../images/background.png"); // 背景
	t3.loadFromFile("../images/frame.png");		 // 前景

	Sprite tiles(t1), background(t2), frame(t3); // 创建 精灵 加载纹理
	// tiles.setTextureRect(IntRect(0, 0, 18, 18)); // 选择纹理区域 (一个方块)
	makeblockS();

	//--------------------------------------------------------------------
	Font font;
	// 加载字体
	if (!font.loadFromFile("../FiraCode-Light.ttf"))
		return -1;
	int m = 0;
	sf::String score = "score:";
	Text text(score + "\n0", font);
	// 设置字体大小
	text.setCharacterSize(20);
	// 设置填充颜色
	text.setFillColor(Color::Cyan);
	// 位置
	text.setPosition(230, 200);
	//--------------------------------------------------------------------
	int dx = 0;		 // 偏移
	bool rotate = 0; // 旋转
	int colorNum = 1;
	float timer = 0, delay = 0.3;

	Clock clock;

	int run = 1;
	// window.display(); 显示窗口(刷新窗口)
	// 窗口开着
	while (window.isOpen())
	{
		// 返回自上次调用restart（）以来经过的时间
		float time = clock.getElapsedTime().asSeconds();
		// 重新启动时钟此功能将时间计数器归零。它还返回时钟启动后经过的时间。
		clock.restart();

		timer += time; // 累加时间

		Event e;
		// 用于将当前事件（例如 鼠标，键盘）传递给event变量e
		while (window.pollEvent(e))
		{
			// 窗口关闭的事件
			if (e.type == Event::Closed)
				window.close();
			// 键盘事件
			if (e.type == Event::KeyPressed)
			{
				if (e.key.code == Keyboard::Escape)
					window.close();
				if (e.key.code == Keyboard::Space)
					run = 1 - run;
				if (e.key.code == Keyboard::Up) // 上键旋转
					rotate = true;
				else if (e.key.code == Keyboard::Left) // 左右偏移
					dx = -1;
				else if (e.key.code == Keyboard::Right)
					dx = 1;
			}
		}
		if (run)
		{
			if (Keyboard::isKeyPressed(Keyboard::Down))
				delay = 0.05; // 缩短下落等待时间 加速下落

			//// <- 移动 -> /// Move
			if (dx != 0)
			{
				for (int i = 0; i < 4; i++)
				{
					// 记录变更前的坐标
					b[i] = a[i];
					// 变更坐标
					a[i].x += dx;
				}
				// 左右到达边界时 使它回到坐标变更之前
				if (!check())
					for (int i = 0; i < 4; i++)
						a[i] = b[i];
			}

			//////旋转//////
			if (rotate)
			{
				/*
					坐标旋转公式
					中心[xc,yc]
					点[x,y]
					转后点[x1,y1]
					x1=xc-y+yc
					y1=yc+x-xc
					ps: (2,1) 绕 (1,0) 转90°后(0,1)[0=1-1+0,1=0+2-1]
					ps: (2,1) 绕 (0,1) 转90°后(0,3)[0=0-1+1,1=1+2-0]
				*/
				// 旋转中心
				Point p = a[1]; // center of rotation
				for (int i = 0; i < 4; i++)
				{
					int x = a[i].y - p.y;
					int y = a[i].x - p.x;
					a[i].x = p.x - x;
					a[i].y = p.y + y;
				}
				// 旋转到达边界时 使它回到上次坐标
				if (!check())
					for (int i = 0; i < 4; i++)
						a[i] = b[i];
			}

			///////Tick//////下落
			// timer 累加到 0.3(delay)秒后，下落一格
			if (timer > delay)
			{
				for (int i = 0; i < 4; i++)
				{
					b[i] = a[i]; // 记录变更前的坐标
					a[i].y += 1;
				}

				if (!check()) // 下落到达边界时
				{
					for (int i = 0; i < 4; i++)
						field[b[i].y][b[i].x] = colorNum; // 标记方块 记录该位置的颜色值

					colorNum = 1 + rand() % 7; // 随机颜色7种颜色的方块
					// int n = rand() % 7;		   // 随机产生方块
					// // 计算出每个形状方块的相对坐标(左上角为[0,0])
					// // 每个形状有4块 x可取{0，1} y可取{0,1,2 3}
					// for (int i = 0; i < 4; i++) // nb!!
					// {
					// 	// 对2取余 得到在第几列，0或1
					// 	a[i].x = 3 + figures[n][i] % 2; // +3 使初始位置在中间
					// 	// 对2取商 得到在第几行，0~3
					// 	a[i].y = figures[n][i] / 2;
					// 	// ps: 7描述时在右下角，所以在第二列，第三行 [1,3]
					// }
					makeblockS();
				}
				timer = 0; // 计时清空
			}
			///////检查 消除//////////
			int k = M - 1;
			for (int i = M - 1; i > 0; i--) // 行遍历
			{
				int count = 0;
				for (int j = 0; j < N; j++) // 列遍历
				{
					if (field[i][j]) // 不为空
						count++;
					field[k][j] = field[i][j]; // 记录第k行
				}
				// 填满时k不移动，则下一次遍历会将其覆盖
				if (count < N) // 没有填满
					k--;	   // 行数上移
				else
					m++; // 得分+1
			}

			dx = 0;		 // 偏移状态置零
			rotate = 0;	 // 旋转状态置零
			delay = 0.3; // 下落等待时间初始值

			/////////draw//////////
			window.clear(Color::White); // 擦除窗口（填充白色，默认黑色)
			window.draw(background);
			for (int i = 0; i < M; i++) // 遍历场景
				for (int j = 0; j < N; j++)
				{
					if (field[i][j] == 0) // 场景位置不为空时
						continue;
					// field 中存储了对应位置的颜色
					tiles.setTextureRect(IntRect(field[i][j] * 18, 0, 18, 18));
					tiles.setPosition(j * 18, i * 18); // 输出该位置
					tiles.move(28, 31);				   // offset 偏移设置 对对象所有坐标都有效
					window.draw(tiles);				   // 绘制精灵
				}
			for (int i = 0; i < 4; i++)
			{
				tiles.setTextureRect(IntRect(colorNum * 18, 0, 18, 18)); // 选择纹理区域 (一个方块)
				// Shape :: setPosition（）函数可以改变形状的位置
				tiles.setPosition(a[i].x * 18, a[i].y * 18); // 根据相对位置输出位置 ，每块是18像素，以18为单位改变
				tiles.move(28, 31);							 // offset 偏移设置 对对象所有坐标都有效
				window.draw(tiles);							 // 绘制精灵
			}

			window.draw(frame);
			text.setString(score + "\n" + to_string(m) + "\nby\nYzHCat");
			window.draw(text);
			window.display(); // 刷新窗口
		}
	}

	return 0;
}
